package controller;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import utils.JsonUtils;

public class Controller {
	private CloseableHttpClient httpClient;
	private HttpClientContext context;
	private List<NameValuePair> nvps;
	private boolean sign = true;

	public static void main(String args[]) throws KeyManagementException,
			NoSuchAlgorithmException, IOException {
		Controller buyTicket = new Controller();
		buyTicket.getTicket();
	}

	public Controller() throws NoSuchAlgorithmException, KeyManagementException {
		init();
	}

	public void init() throws NoSuchAlgorithmException, KeyManagementException {
		X509TrustManager xtm = new X509TrustManager() { // 创建TrustManager
			public void checkClientTrusted(X509Certificate[] chain,
					String authType) {
			}

			public void checkServerTrusted(X509Certificate[] chain,
					String authType) {
			}

			public X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};
		// TLS1.0与SSL3.0基本上没有太大的差别，可粗略理解为TLS是SSL的继承者，但它们使用的是相同的SSLContext
		SSLContext ctx = SSLContext.getInstance("TLS");
		// 使用TrustManager来初始化该上下文，TrustManager只是被SSL的Socket所使用
		ctx.init(null, new TrustManager[] { xtm }, null);
		// 创建SSLSocketFactory
		SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
				ctx);
		// 通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
		httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
				.build();
		context = new HttpClientContext();
		nvps = new ArrayList<NameValuePair>();
	}

	public BufferedImage getRandCode(String url) {
		CloseableHttpResponse response = null;
		try {
			HttpGet get = new HttpGet(url);
			response = httpClient.execute(get, context);
			HttpEntity entity = response.getEntity();
			BufferedImage image = ImageIO.read(entity.getContent());
			EntityUtils.consume(entity);
			return image;
		} catch (Exception e) {
			e.printStackTrace();
			if (response != null) {
				try {
					response.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return null;
	}

	public String sendGet(String url) {
		CloseableHttpResponse response = null;
		String content = null;
		try {
			HttpGet get = new HttpGet(url);
			response = httpClient.execute(get, context);
			HttpEntity entity = response.getEntity();
			if (url.contains("getPassCodeNew")) {
				// EntityUtils.toString会关闭流！！！
				// getRandCode(entity.getContent());
				byte b[] = new byte[1024];
				entity.getContent().read(b);
				return b.toString();
			} else {
				content = EntityUtils.toString(entity);
			}
			EntityUtils.consume(entity);
			return content;
		} catch (Exception e) {
			e.printStackTrace();
			if (response != null) {
				try {
					response.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return content;
	}

	private void getRandCode(InputStream in) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(in);
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream("d:\\12306\\1.jpg"));
		int i = -1;
		while ((i = bis.read()) != -1) {
			bos.write(i);
		}
		bis.close();
		bos.close();
	}

	public String sendPost(String url, List<NameValuePair> nvps) {
		CloseableHttpResponse response = null;
		String content = null;
		try {
			HttpPost post = new HttpPost(url);
			if (nvps != null) {
				post.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
			}
			if (post.getEntity() != null) {
				System.out.println(EntityUtils.toString(post.getEntity()));
			}
			response = httpClient.execute(post, context);
			HttpEntity entity = response.getEntity();
			content = EntityUtils.toString(entity);
			EntityUtils.consume(entity);
			return content;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return content;
	}

	public String login(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String queryLeftTickets(String date, String from, String to,
			String isAdult) {
		return sendGet("https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date="
				+ date
				+ "&leftTicketDTO.from_station="
				+ from
				+ "&leftTicketDTO.to_station="
				+ to
				+ "&purpose_codes="
				+ isAdult);
	}

	public void checkUser(String url) {
		sendPost(url, null);
	}

	public String submitOrderRequest(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String initDc(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String extractToken(String key, String content, int type) {
		String startSign = "'";
		String endSign = ";";
		if (type == 2) {
			startSign = "{";
		}
		int i = -1;
		if ((i = content.indexOf(key)) != -1) {
			content = content.substring(i);
			int start = content.indexOf(startSign);
			int end = content.indexOf(endSign);
			return content.substring(start, end);
		}
		return "notFound";
	}

	public String checkRandCodeAnsyn(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String checkOrderInfo(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String getQueueCount(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String confirmSingleForQueue(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	public String getPassengerDTOs(String url, List<NameValuePair> nvps) {
		return sendPost(url, nvps);
	}

	// 客户端调用的方法
	public void getTicket() throws IOException {
		Scanner sc = new Scanner(System.in);
		String randCode = "";
		String resInfo = "";
		// 得到验证码
		print("-------开始验证码----------");
		sendGet("https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew.do?module=login&rand=sjrand");
		System.out.println("Please input the randCode:");
		randCode = sc.next();
		// 登陆
		print("-------开始登陆----------");
		String username = "";
		String password = "";
		nvps.add(new BasicNameValuePair("loginUserDTO.user_name", username));
		nvps.add(new BasicNameValuePair("userDTO.password", password));
		nvps.add(new BasicNameValuePair("randCode", randCode));
		login("https://kyfw.12306.cn/otn/login/loginAysnSuggest", nvps);
		// 至此登陆成功，然后进行购票
		print("-------开始查询----------");
		String date = "2014-04-11";
		String from = "HZH";
		String to = "XMS";
		String isAdult = "ADULT";
		resInfo = queryLeftTickets(date, from, to, isAdult);
		JSONObject jsonObject = JSONObject.fromObject(resInfo);
		JSONArray jsonArray = jsonObject.getJSONArray("data");
		int jsonLen = jsonArray.size();
		String[] secretStrs = new String[jsonLen];
		for (int j = 0; j < jsonLen; j++) {
			JSONObject train = jsonArray.getJSONObject(j).getJSONObject(
					"queryLeftNewDTO");
			secretStrs[j] = jsonArray.getJSONObject(j).getString("secretStr");
			secretStrs[j] = URLDecoder.decode(secretStrs[j], "UTF-8");
			print("车次:" + train.getString("station_train_code") + ", 出发:"
					+ train.getString("from_station_name") + ", 到达："
					+ train.getString("to_station_name") + ", 软卧票剩余:"
					+ train.getString("rw_num") + ", 硬卧票剩余:"
					+ train.getString("yw_num"));
		}
		// 预订之前还要再次检查是否登陆
		print("--------再次检查是否登陆-----------");
		checkUser("https://kyfw.12306.cn/otn/login/checkUser");
		// 提交预订请求
		print("----------提交预订请求-----------");
		System.out.println("Please input the train number:");
		int trainNum = sc.nextInt();
		String secretStr = secretStrs[trainNum];
		nvps.add(new BasicNameValuePair("secretStr", secretStr));
		nvps.add(new BasicNameValuePair("train_date", date));// TODO
		nvps.add(new BasicNameValuePair("back_train_date", date));
		nvps.add(new BasicNameValuePair("tour_flag", "dc"));// TODO
		nvps.add(new BasicNameValuePair("purpose_codes", isAdult));
		// 这2个参数在这里其实没有用到，只需要secretStr就可以来确定
		nvps.add(new BasicNameValuePair("query_from_station_name", "杭州东"));
		nvps.add(new BasicNameValuePair("query_to_station_name", "厦门"));
		nvps.add(new BasicNameValuePair("undefined", ""));// TODO
		submitOrderRequest(
				"https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest", nvps);
		// 初始化预订页面(Dc为单程订票界面)
		print("--------------initDc---------------");
		String content = initDc(
				"https://kyfw.12306.cn/otn/confirmPassenger/initDc", null);
		String token = extractToken("globalRepeatSubmitToken", content, 1);
		String ticketInfoForPassengerForm = extractToken(
				"ticketInfoForPassengerForm", content, 2);
		jsonObject = JSONObject.fromObject(ticketInfoForPassengerForm);
		String leftTicketStr = jsonObject.getString("leftTicketStr");
		String purpose_codes = jsonObject.getString("purpose_codes");
		String train_location = jsonObject.getString("train_location");
		String key_check_isChange = jsonObject.getString("key_check_isChange");
		JSONObject orderRequestDTO = jsonObject
				.getJSONObject("orderRequestDTO");
		String station_train_code = orderRequestDTO
				.getString("station_train_code");
		String fromStationTelecode = orderRequestDTO
				.getString("from_station_telecode");
		String toStationTelecode = orderRequestDTO
				.getString("to_station_telecode");
		String train_no = orderRequestDTO.getString("train_no");

		// 测试数据
		String oldPassengerStr = "";
		String passengerTicketStr = "";

		// 得到并检查预订验证码
		sendGet("https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=randp");
		System.out.println("Please insert the random code:");
		randCode = sc.next();
		System.out.println("------checkRandCodeAnsyn--------");
		nvps.add(new BasicNameValuePair("REPEAT_SUBMIT_TOKEN", token));
		nvps.add(new BasicNameValuePair("rand", "randp"));
		nvps.add(new BasicNameValuePair("randCode", randCode));
		checkRandCodeAnsyn(
				"https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn",
				nvps);

		// CheckOrderInfo
		System.out.println("--------checkOrderInfo-----------");
		nvps.add(new BasicNameValuePair("cancel_flag", "2"));
		nvps.add(new BasicNameValuePair("REPEAT_SUBMIT_TOKEN", token));
		nvps.add(new BasicNameValuePair("bed_level_order_num",
				"000000000000000000000000000000"));
		nvps.add(new BasicNameValuePair("oldPassengerStr", oldPassengerStr));
		nvps.add(new BasicNameValuePair("passengerTicketStr",
				passengerTicketStr));
		nvps.add(new BasicNameValuePair("randCode", randCode));
		nvps.add(new BasicNameValuePair("tour_flag", "dc"));
		resInfo = checkOrderInfo(
				"https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo",
				nvps);
		print(resInfo);

		// getQueueCount
		print("------------getQueueCount------------");
		nvps.add(new BasicNameValuePair("REPEAT_SUBMIT_TOKEN", token));
		nvps.add(new BasicNameValuePair("fromStationTelecode",
				fromStationTelecode));
		nvps.add(new BasicNameValuePair("leftTicket", leftTicketStr));
		nvps.add(new BasicNameValuePair("purpose_codes", purpose_codes));
		nvps.add(new BasicNameValuePair("seatType", "0")); // TODO
		nvps
				.add(new BasicNameValuePair("stationTrainCode",
						station_train_code));
		nvps
				.add(new BasicNameValuePair("toStationTelecode",
						toStationTelecode));
		nvps.add(new BasicNameValuePair("train_date",
				"Mon Mar 31 2014 00:00:00 GMT+0800")); // TODO
		nvps.add(new BasicNameValuePair("train_no", train_no));
		resInfo = getQueueCount(
				"https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount",
				nvps);
		print(resInfo);

		// confirmSingleForQueue
		print("-----------confirmSingleForQueue---------------");
		nvps.add(new BasicNameValuePair("REPEAT_SUBMIT_TOKEN", token));
		nvps.add(new BasicNameValuePair("key_check_isChange",
				key_check_isChange));
		nvps.add(new BasicNameValuePair("leftTicketStr", leftTicketStr));
		nvps.add(new BasicNameValuePair("oldPassengerStr", oldPassengerStr));
		nvps.add(new BasicNameValuePair("passengerTicketStr",
				passengerTicketStr));
		nvps.add(new BasicNameValuePair("purpose_codes", purpose_codes));
		nvps.add(new BasicNameValuePair("randCode", randCode));
		nvps.add(new BasicNameValuePair("train_location", train_location));
		resInfo = confirmSingleForQueue(
				"https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue",
				nvps);
		print(resInfo);

		print("----------------Finish-------------------");
		finish();
	}

	private void print(String str) {
		if (sign) {
			System.out.println(str);
		}
	}

	private void finish() throws IOException {
		httpClient.close();
	}
}
